/////////////////////////////////////////////////////////////////////////////
// This file is part of the "Java-DAP" project, a Java implementation
// of the OPeNDAP Data Access Protocol.
//
// Copyright (c) 2007 OPeNDAP, Inc.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
/////////////////////////////////////////////////////////////////////////////
using System;
using NDODS.DAP;
using NDODS.DAP.BaseTypes;
using NDODS.DAP.Primitives;

namespace NDODS.DAP.Server
{
	
	/// <summary> Holds a OPeNDAP Server <code>List</code> value.
	/// 
	/// </summary>
	/// <author>  ndp
	/// </author>
	/// <version>  $Revision: 15901 $
	/// </version>
	/// <seealso cref="BaseType">
	/// </seealso>
	[Serializable]
	public abstract class SDList:DList, IServerMethods, IRelOps
	{
		//UPGRADE_NOTE: Respective javadoc comments were merged.  It should be changed in order to comply with .NET documentation conventions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1199'"
		/// <summary> Get the value of the IsRead property.
		/// 
		/// </summary>
		/// <returns> <code>true</code> if the variable has been Read,
		/// <code>false</code> otherwise.
		/// </returns>
		/// <seealso cref="Read">
		/// </seealso>
		/// <seealso cref="setRead (bool)">
		/// </seealso>
		/// <summary> Set the IsRead property. A normal variable is Read using the
		/// <code>Read()</code> method. Once Read the <em>IsRead</em> property is
		/// <code>true</code>. Use this function to manually set the property
		/// value. By default this property is false.
		/// 
		/// </summary>
		/// <param name="state"><code>true</code> if the variable has been Read,
		/// <code>false</code> otherwise.
		/// </param>
		/// <seealso cref="isRead()">
		/// </seealso>
		/// <seealso cref="Read">
		/// </seealso>
		virtual public bool IsRead
		{
			get
			{
				return (ReadMe);
			}
			
			set
			{
				ReadMe = value;
			}
			
		}
		
		private const bool _Debug = false;
		
		private bool Project;
		private bool Synthesized;
		private bool ReadMe;
		
		/// <summary> Constructs a new <code>SDList</code>.</summary>
		public SDList():base()
		{
			Project = false;
			Synthesized = false;
			ReadMe = false;
		}
		
		/// <summary> Constructs a new <code>SDList</code> with name <code>n</code>.
		/// 
		/// </summary>
		/// <param name="n">the name of the variable.
		/// </param>
		public SDList(System.String n):base(n)
		{
			Project = false;
			Synthesized = false;
			ReadMe = false;
		}
		
		/// <summary> Write the variable's declaration in a C-style syntax. This
		/// function is used to create textual representation of the Data
		/// Descriptor Structure (DDS).  See <em>The OPeNDAP User Manual</em> for
		/// information about this structure.
		/// 
		/// </summary>
		/// <param name="os">         The <code>StreamWriter</code> on which to Print the
		/// declaration.
		/// </param>
		/// <param name="space">      Each line of the declaration will begin with the
		/// characters in this string.  Usually used for leading spaces.
		/// </param>
		/// <param name="Print_semi"> a bool value indicating whether to Print a
		/// semicolon at the end of the declaration.
		/// </param>
		/// <param name="constrained">a bool value indicating whether to Print
		/// the declartion dependent on the projection information. <b>This
		/// is only used by Server side code.</b>
		/// </param>
		/// <seealso cref="BaseType.printDecl(StreamWriter, String, bool, bool)">
		/// </seealso>
		public override void  PrintDeclaration(System.IO.StreamWriter os, System.String space, bool print_semi, bool constrained)
		{
			if (constrained && !Project)
				return ;
			
			// BEWARE! Since PrintDeclaration()is (multiple) overloaded in BaseType
			// and all of the different signatures of PrintDeclaration() in BaseType
			// lead to one signature, we must be careful to override that
			// SAME signature here. That way all calls to PrintDeclaration() for
			// this object lead to this implementation.
			
			// Also, since PrintDeclaration()is (multiple) overloaded in BaseType
			// and all of the different signatures of PrintDeclaration() in BaseType
			// lead to the signature we are overriding here, we MUST call
			// the PrintDeclaration with the SAME signature THROUGH the super class
			// reference (assuming we want the super class functionality). If
			// we do otherwise, we will create an infinte call loop. OOPS!
			
			base.PrintDeclaration(os, space, print_semi, constrained);
		}
		
		
		/// <summary> Prints the value of the variable, with its declaration.  This
		/// function is primarily intended for debugging OPeNDAP applications and
		/// text-based clients such as geturl.
		/// <p/>
		/// <h2> Important Note</h2>
		/// This method overrides the BaseType method of the same name and
		/// type signature and it significantly changes the behavior for all versions
		/// of <code>PrintValue()</code> for this type:
		/// <b><i> All the various versions of printVal() will only
		/// Print a value, or a value with declaration, if the variable is
		/// in the projection.</i></b>
		/// <br>
		/// <br>In other words, if a call to
		/// <code>isProject()</code> for a particular variable returns
		/// <code>true</code> then <code>printVal()</code> will Print a value
		/// (or a declaration and a value).
		/// <br>
		/// <br>If <code>isProject()</code> for a particular variable returns
		/// <code>false</code> then <code>printVal()</code> is basically a No-Op.
		/// <br>
		/// <br>
		/// 
		/// </summary>
		/// <param name="os">          the <code>StreamWriter</code> on which to Print the value.
		/// </param>
		/// <param name="space">       this value is passed to the <code>PrintDeclaration</code> method,
		/// and controls the leading spaces of the output.
		/// </param>
		/// <param name="Print_decl_p">a bool value controlling whether the
		/// variable declaration is printed as well as the value.
		/// </param>
		/// <seealso cref="BaseType.printVal(StreamWriter, String, bool)">
		/// </seealso>
		/// <seealso cref="ServerMethods.isProject()">
		/// </seealso>
		public override void  PrintValue(System.IO.StreamWriter os, System.String space, bool print_decl_p)
		{
			if (!Project)
				return ;
			
			PrimitiveVector pv = PrimitiveVector;
			
			if (pv is BaseTypePrimitiveVector)
			{
				
				BaseTypePrimitiveVector vals = (BaseTypePrimitiveVector) pv;
				
				if (print_decl_p)
				{
					PrintDeclaration(os, space, false, true);
					os.Write(" = ");
				}
				
				os.Write("{ ");
				//vals.printVal(os, "");
				
				IServerMethods sm;
				int len = vals.Length;
				for (int i = 0; i < len - 1; i++)
				{
					sm = (IServerMethods) vals.getValue(i);
					if (sm.isProject())
					{
						((BaseType) sm).PrintValue(os, "", false);
						os.Write(", ");
					}
				}
				// Print last value, if any, without trailing comma
				if (len > 0)
				{
					sm = (IServerMethods) vals.getValue(len - 1);
					if (sm.isProject())
					{
						((BaseType) sm).PrintValue(os, "", false);
					}
				}
				
				
				os.Write("}");
				
				if (print_decl_p)
				{
					//UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
					os.WriteLine(";");
				}
			}
			else
			{
				base.PrintValue(os, space, print_decl_p);
			}
		}
		
		// --------------- Projection Interface
		
		/// <summary> Set the state of this variable's projection. <code>true</code> means
		/// that this variable is part of the current projection as defined by
		/// the current constraint expression, otherwise the current projection
		/// for this variable should be <code>false</code>.
		/// 
		/// </summary>
		/// <param name="state"><code>true</code> if the variable is part of the current
		/// projection, <code>false</code> otherwise.
		/// </param>
		/// <param name="all">  This parameter has no effect for this type of variable.
		/// </param>
		/// <seealso cref="CEEvaluator">
		/// </seealso>
		public virtual void  setProject(bool state, bool all)
		{
			Project = state;
			PrimitiveVector vals = PrimitiveVector;
			((IServerMethods) (vals.Template)).setProject(state, all);
			
			//		if(vals instanceof BaseTypePrimitiveVector){
			//			System.out.println("Setting Projecttion for List member: "+
			//			((BaseType)((BaseTypePrimitiveVector)vals).getTemplate()).getTypeName() + " " +
			//			((BaseType)((BaseTypePrimitiveVector)vals).getTemplate()).getName());
			//			((ServerMethods)((BaseTypePrimitiveVector)vals).getTemplate()).setProject(state);
			//		}
		}
		
		/// <summary> Set the state of this variable's projection. <code>true</code> means
		/// that this variable is part of the current projection as defined by
		/// the current constraint expression, otherwise the current projection
		/// for this variable should be <code>false</code>.
		/// 
		/// </summary>
		/// <param name="state"><code>true</code> if the variable is part of the current
		/// projection, <code>false</code> otherwise.
		/// </param>
		/// <seealso cref="CEEvaluator">
		/// </seealso>
		public virtual void  setProject(bool state)
		{
			setProject(state, true);
		}
		
		/// <summary> Check the projection state of this variable.
		/// Is the given variable marked as projected? If the variable is listed
		/// in the projection part of a constraint expression, then the CE parser
		/// should mark it as <em>projected</em>. When this method is called on
		/// such a variable it should return <code>true</code>, otherwise it
		/// should return <code>false</code>.
		/// 
		/// </summary>
		/// <returns> <code>true</code> if the variable is part of the current
		/// projections, <code>false</code> otherwise.
		/// </returns>
		/// <seealso cref="CEEvaluator">
		/// </seealso>
		/// <seealso cref="setProject(bool)">
		/// </seealso>
		public virtual bool isProject()
		{
			return (Project);
		}
		
		// --------------- RelOps Interface
		
		/// <summary> The RelOps interface defines how each type responds to relational
		/// operators. Most (all?) types will not have sensible responses to all of
		/// the relational operators (e.g. DList won't know how to match a regular
		/// expression but DString will). For those operators that are nonsensical a
		/// class should throw InvalidOperatorException.
		/// </summary>
		
		public virtual bool equal(BaseType bt)
		{
			throw new InvalidOperatorException("Equals (=) operator does not work with the type SDList!");
		}
		
		public virtual bool not_equal(BaseType bt)
		{
			throw new InvalidOperatorException("Not Equals (!=) operator does not work with the type SDList!");
		}
		
		public virtual bool greater(BaseType bt)
		{
			throw new InvalidOperatorException("Greater Than (>)operator does not work with the type SDList!");
		}
		
		public virtual bool greater_eql(BaseType bt)
		{
			throw new InvalidOperatorException("GreaterThan or equals (<=) operator does not work with the type SDList!");
		}
		
		public virtual bool less(BaseType bt)
		{
			throw new InvalidOperatorException("LessThan (<) operator does not work with the type SDList!");
		}
		
		public virtual bool less_eql(BaseType bt)
		{
			throw new InvalidOperatorException("LessThan oe equals (<=) operator does not work with the type SDList!");
		}
		
		public virtual bool regexp(BaseType bt)
		{
			throw new InvalidOperatorException("Regular Expression's don't work with the type SDList!");
		}
		
		// --------------- FileIO Interface
		
		/// <summary> Set the Synthesized property.
		/// 
		/// </summary>
		/// <param name="state">If <code>true</code> then the variable is considered a
		/// synthetic variable and no part of OPeNDAP will ever try to Read it from a
		/// file, otherwise if <code>false</code> the variable is considered a
		/// normal variable whose value should be Read using the
		/// <code>Read()</code> method. By default this property is false.
		/// </param>
		/// <seealso cref="IsSynthesized">
		/// </seealso>
		/// <seealso cref="Read">
		/// </seealso>
		public virtual void  setSynthesized(bool state)
		{
			Synthesized = state;
		}
		
		/// <summary> Get the value of the Synthesized property.
		/// 
		/// </summary>
		/// <returns> <code>true</code> if this is a synthetic variable,
		/// <code>false</code> otherwise.
		/// </returns>
		public virtual bool IsSynthesized()
		{
			return (Synthesized);
		}
		
		/// <summary> IsRead a value from the named dataset for this variable.
		/// <h2>Caution:</h2>
		/// When reading Lists of sequences (children of DSequence) it is crucial
		/// that it be handled with great care. Sequences have been implemented so that
		/// only one instance (or row if you will) is retained in memory at a given time.
		/// In order to correctly Read a List of Sequences the Read() method for the
		/// List must create an instance of the Sequence for each member of the List, typically
		/// by repeatedly cloning the template variable in the PrimitiveVector. The important next
		/// step is: DO NOT attempt to Read any data into the sequences from within the Read()
		/// method of the List. The sequence's data will get Read, and constraint expressions
		/// applied when the serialze() method of the List calls the serialize() method
		/// of the Sequence, which in turn calls the Read method of the Sequence. Good Luck!
		/// 
		/// </summary>
		/// <param name="datasetName">String identifying the file or other data store
		/// from which to Read a vaue for this variable.
		/// </param>
		/// <param name="specialO">   This <code>Object</code> is a goody that is used by Server implementations
		/// to deliver important, and as yet unknown, stuff to the Read method. If you
		/// don't need it, make it a <code>null</code>.
		/// </param>
		/// <returns> <code>true</code> if more data remains to be Read, otherwise
		/// <code>false</code>. This is an abtsract method that must be implemented
		/// as part of the installation/localization of a OPeNDAP server.
		/// </returns>
		/// <throws>  IOException </throws>
		/// <throws>  EOFException </throws>
		
		public abstract bool Read(System.String datasetName, System.Object specialO);
		
		
		/// <summary> <p/>
		/// Server-side serialization for OPeNDAP variables (sub-classes of
		/// <code>BaseType</code>).
		/// This does not send the entire class as the Java <code>Serializable</code>
		/// interface does, rather it sends only the binary data values. Other software
		/// is responsible for sending variable type information (see <code>DDS</code>).
		/// </p><p>
		/// Writes data to a <code>DataOutputStream</code>. This method is used
		/// on the server side of the OPeNDAP client/server connection, and possibly
		/// by GUI clients which need to download OPeNDAP data, manipulate it, and
		/// then re-save it as a binary file.
		/// </p>
		/// <h2>Caution:</h2>
		/// When serializing lists of sequences (children of DSequence) it is crucial
		/// that it be handled with great care. Sequences have been implemented so that
		/// only one instance (or row if you will) is retained in memory at a given time.
		/// In order to correctly serialize a list of sequences the Read() method for the
		/// List must create an instance of the sequence for each member of the list, typically
		/// by repeatedly cloning the template variable in the PrimitiveVector. The important next
		/// step is to NOT attempt to Read any data into the sequences from within the Read()
		/// method of the List. The sequence's data will get Read, and constraint expressions
		/// applied when the serialze() method of the List calls the serialize() method
		/// of the sequence. Good Luck!
		/// 
		/// </summary>
		/// <param name="sink">a <code>DataOutputStream</code> to write to.
		/// </param>
		/// <throws>  IOException thrown on any <code>OutputStream</code> exception. </throws>
		/// <seealso cref="BaseType">
		/// </seealso>
		/// <seealso cref="DDS">
		/// </seealso>
		/// <seealso cref="ServerDDS">
		/// </seealso>
		//UPGRADE_TODO: Class 'java.io.DataOutputStream' was converted to 'System.IO.BinaryWriter' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioDataOutputStream'"
		public virtual void  serialize(System.String dataset, System.IO.BinaryWriter sink, CEEvaluator ce, System.Object specialO)
		{
			
			PrimitiveVector vals = PrimitiveVector;
			
			if (_Debug)
				System.Console.Out.WriteLine("\nSTARTED.............SDList.serialize(" + dataset + ")");
			
			if (!IsRead)
				Read(dataset, specialO);
			
			if (vals.Template is DSequence || ce.evalClauses(specialO))
			{
				
				// Because arrays of primitive types (ie int32, float32, byte, etc) are
				// handled in the C++ core using the XDR package we must write the
				// length twice for those types. For BaseType vectors, we should write
				// it only once. This is in effect a work around for a bug in the C++
				// core as the C++ core does not consume 2 length values for thge
				// BaseType vectors. Bummer...
				int length = vals.Length;
				sink.Write(length);
				
				// Gotta check for this to make sure that DConstructor types
				// (Especially SDSequence) get handled correctly!!!
				if (vals is BaseTypePrimitiveVector)
				{
					for (int i = 0; i < length; i++)
					{
						IServerMethods sm = (IServerMethods) ((BaseTypePrimitiveVector) vals).getValue(i);
						sm.serialize(dataset, sink, ce, specialO);
					}
				}
				else
				{
					
					// because both XDR and OPeNDAP Read the length, we must write it twice
					sink.Write(length);
					vals.Externalize(sink);
				}
			}
			if (_Debug)
				System.Console.Out.WriteLine("\nFINISHED............SDList.serialize(" + dataset + ") ");
		}
	}
}